# 错误处理
# try...except...finally...
try:
    print('try...')
    r = 10/0
    print('result:', r)  # 不执行
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')
# 当我们认为某些代码可能会出错时，就可以用try来运行这段代码，如果执行出错，则后续代码不会继续执行，而是直接跳转至错误处理代码，即except语句块，执行完except后，如果有finally语句块，则执行finally语句块，至此，执行完毕。
print('==============分割线=================')
try:
    print('try...')
    r = 10/2
    print('result:', r)  # 执行
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')
# 由于没有错误发生，所以except语句块不会被执行，但是finally如果有，则一定会被执行（可以没有finally语句）
print('==============分割线=================')
try:
    print('try...')
    r = 10 / int('a')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
finally:
    print('finally...')
print('END')
#可以有多个except来捕获不同类型的错误
print('==============分割线=================')
try:
    print('try...')
    r = 10 / int('2')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')
#所有的错误类型都继承自BaseException，所以在使用except时需要注意的是，它不但捕获该类型的错误，还把其子类也“一网打尽”。
#常见的错误类型和继承关系看这里：https://docs.python.org/3/library/exceptions.html#exception-hierarchy
print('==============分割线=================')
#使用try...except捕获错误还有一个巨大的好处，就是可以跨越多层调用，比如函数main()调用bar()，bar()调用foo()，结果foo()出错了，这时，只要main()捕获到了，就可以处理：
def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar('0')
    except Exception as e:
        print('Error:', e)
    finally:
        print('finally...')
print(foo(100))
print('==============分割线=================')
#调用栈
# err.py:
'''def foo1(s):
    return 10 / int(s)

def bar1(s):
    return foo1(s) * 2

def main1():
    bar1('0')
main1()'''
# 出错的时候，一定要分析错误的调用栈信息，才能定位错误的位置。
print('==============分割线=================')
#记录错误
# err_logging.py

import logging

def foo2(s):
    return 10 / int(s)

def bar2(s):
    return foo2(s) * 2

def main2():
    try:
        bar2('0')
    except Exception as e:
        logging.exception(e)
main2()
print('END')
#通过配置，logging还可以把错误记录到日志文件里，方便事后排查。
print('==============分割线=================')
#抛出错误
# err_raise.py
'''class FooError(ValueError):
    pass
def foo3(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n
foo3('0')'''
print('==============分割线=================')
# err_reraise.py

def foo4(s):
    n = int(s)
    if n==0:
        raise ValueError('invalid value: %s' % s)
    return 10 / n
def bar4():
    try:
        foo4('0')
    except ValueError as e:
        print('ValueError!')
        raise
bar4()
#raise语句如果不带参数，就会把当前错误原样抛出。此外，在except中raise一个Error，还可以把一种类型的错误转化成另一种类型：